package com.smart.client.core.handler;

import com.smart.client.core.client.IClient;
import com.smart.client.core.notice.EventNotice;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * Created By Li Jiaxi
 * <br/>Date: 2023/4/12 10:37
 * <br/>Description:
 */
@Slf4j
// @Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class NettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter {

    private final EventNotice eventNotice;

    private final IClient client;

    /**
     * 从服务端收到新的数据时，这个方法会在收到消息时被调用
     *
     * @param ctx ChannelHandlerContext
     * @param msg 服务端推送的消息
     */
    @Override
    public void channelRead(@NonNull ChannelHandlerContext ctx, @NonNull Object msg) throws Exception {
        String data = ((ByteBuf) msg).toString(StandardCharsets.UTF_8);
        log.debug("Received server data: {}", data);
        eventNotice.triggerNotice(data);
        //回应服务端
        ctx.write("I got server message thanks server!");
    }

    /**
     * 从服务端收到新的数据、读取完成时调用
     *
     * @param ctx ChannelHandlerContext
     */
    @Override
    public void channelReadComplete(@NonNull ChannelHandlerContext ctx) throws Exception {
        log.debug("channelReadComplete......{}", ctx.channel().id());
        ctx.flush();
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx   ChannelHandlerContext
     * @param cause 异常
     */
    @Override
    public void exceptionCaught(@NonNull ChannelHandlerContext ctx, @NonNull Throwable cause) throws Exception {
        log.error("exceptionCaught: {}", cause.getMessage());
        cause.printStackTrace();
        ctx.close();//抛出异常，断开与客户端的连接
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx ChannelHandlerContext
     * @throws Exception 异常
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        log.debug("channelActive: {} {} ......", clientIp, ctx.name());
        String data = "{\n" +
                "\"topic\": \"dataReportTopic\",\n" +
                "\"stationCode\": \"ST_001\",\n" +
                "    \"message\": {\n" +
                "        \"stationCode\": \"ST_001\",\n" +
                "        \"groupCode\": \"G_001\",\n" +
                "        \"memsCode\": \"001\",\n" +
                "        \"angleX\": \"0.438\",\n" +
                "        \"angleY\": \"0.588\",\n" +
                "        \"angleZ\": \"0.003\",\n" +
                "        \"reportTime\": \"2022-12-07 16:00:00\"    \n" +
                "}\n" +
                "}\n";
        ByteBuf message = null;
        byte[] req = (data + "#").getBytes();
        for (int i = 0; i < 1; i++) {
            message = Unpooled.buffer(req.length);
            message.writeBytes(req);
            Thread.sleep(10000);
            ctx.writeAndFlush(message);
        }
    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx ChannelHandlerContext
     * @throws Exception 异常
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        ctx.close(); //断开连接时，必须关闭，否则造成资源浪费
        log.debug("channelInactive: {}", clientIp);
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(() -> {
            try {
                client.reconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 1L, TimeUnit.SECONDS);
        super.channelInactive(ctx);
    }
}
